[11-Gin中间件]
一 中间件介绍
Gin 框架允许开发者在处理请求的过程中,加入用户自己的钩子(Hook)函数。这个钩子函 数就叫中间件,中间件适合处理一些公共的业务逻辑,比如登录认证、权限校验、数据分页、 记录日志、耗时统计等。
通俗的讲:中间件就是匹配路由前和匹配路由完成后执行的一系列操作
二 局部中间件
2.1 初识中间件
Gin 中的中间件必须是一个 gin.HandlerFunc 类型,配置路由的时候可以传递多个 func 回调函 数,最后一个 func 回调函数前面触发的方法都可以称为中间件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| package main
import ( "fmt" "github.com/gin-gonic/gin" )
func initMiddleware(c *gin.Context) { fmt.Println("我是一个中间件") } func main() { r := gin.Default() r.GET("/index",initMiddleware, func(c *gin.Context) { c.String(200,"首页") }) r.GET("/home",initMiddleware, func(c *gin.Context) { c.String(200,"home") }) r.Run() }
|
2.2 c.Next()
中间件里面加上 ctx.Next()可以让我们在路由匹配完成后执行一些操作。 比如我们统计一个请求的执行时间。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| package main
import ( "fmt" "github.com/gin-gonic/gin" "time" )
func totalTime(c *gin.Context) { start:=time.Now()
c.Next()
end:=time.Now() fmt.Println("视图函数运行时间为:",end.Sub(start)) } func main() { r := gin.Default() r.GET("/index",totalTime, func(c *gin.Context) { time.Sleep(time.Second*2) c.String(200,"首页") })
r.Run() }
|
2.3 多个中间件执行顺序
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| package main
import ( "fmt" "github.com/gin-gonic/gin" "time" )
func initMiddleware(c *gin.Context) { fmt.Println("我是第一个中间件开始") c.Next() fmt.Println("我是第一个中间件结束") }
func totalTime(c *gin.Context) { fmt.Println("我是第二个中间件开始") start := time.Now() c.Next() end := time.Now() fmt.Println("视图函数运行时间为:", end.Sub(start)) fmt.Println("我是第二个中间件结束") } func main() { r := gin.Default() r.GET("/index", initMiddleware, totalTime, func(c *gin.Context) { fmt.Println("我是视图函数") c.String(200, "首页") })
r.Run() } 我是第一个中间件开始 我是第二个中间件开始 我是视图函数 视图函数运行时间为: 79.142µs 我是第二个中间件结束 我是第一个中间件结束
|
2.4 c.Abort()
Abort 是终止的意思, c.Abort() 表示终止调用该请求的剩余处理程序
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| package main
import ( "fmt" "github.com/gin-gonic/gin" )
func initMiddleware1(c *gin.Context) { fmt.Println("我是第一个中间件开始") fmt.Println("我是第一个中间件结束") } func initMiddleware2(c *gin.Context) { fmt.Println("我是第二个中间件开始") if c.FullPath() == "/index" { c.Abort() } fmt.Println("我是第二个中间件结束") }
func main() { r := gin.Default() r.GET("/index", initMiddleware1, initMiddleware2, func(c *gin.Context) { fmt.Println("我是视图函数-index") c.String(200, "首页") }) r.GET("/home", initMiddleware1, initMiddleware2, func(c *gin.Context) { fmt.Println("我是视图函数-home") c.String(200, "home") })
r.Run() }
我是第一个中间件开始 我是第一个中间件结束 我是第二个中间件开始 我是第二个中间件结束 我是视图函数-home
我是第一个中间件开始 我是第一个中间件结束 我是第二个中间件开始 我是第二个中间件结束
|
三 全局中间件
所有请求都经过此中间件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| package main
import ( "fmt" "github.com/gin-gonic/gin" )
func initMiddleware(ctx *gin.Context) { fmt.Println("全局中间件 通过 r.Use 配置") ctx.Set("name","lqz") ctx.Next() }
func main() { r := gin.Default() r.Use(initMiddleware) r.GET("/index",func(c *gin.Context) { fmt.Println("我是视图函数-index") fmt.Println(c.Get("name")) c.String(200, "首页") })
r.Run() }
|
四 在路由分组中配置中间件
4.1 方式一
1 2 3 4 5 6 7
| shopGroup := r.Group("/shop",initMiddleware) { shopGroup.GET("/index", func(c *gin.Context) { ... }) ... }
|
4.2 方式二
1 2 3 4 5 6 7 8
| shopGroup := r.Group("/shop") shopGroup.Use(initMiddleware) { shopGroup.GET("/index", func(c *gin.Context) { ... }) ... }
|
五 中间件和视图函数之间共享数据
1 2 3 4 5
| ctx.Set("name", "刘清政")
username, _ := ctx.Get("name")
|
六 中间件解决跨域
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| func Cors(c *gin.Context) { c.Header("Access-Control-Allow-Origin", "*") c.Header("Access-Control-Allow-Headers", "Content-Type,AccessToken,X-CSRF-Token, Authorization, Token") c.Header("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, PATCH, DELETE") c.Header("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Content-Type") c.Header("Access-Control-Allow-Credentials", "true") method := c.Request.Method if method == "OPTIONS" { c.AbortWithStatus(http.StatusNoContent) } c.Next() }
|
七 中间件注意事项
6.1 gin 默认中间件
gin.Default()默认使用了 Logger 和 Recovery 中间件,其中:
Logger 中间件将日志写入 gin.DefaultWriter,即使配置了 GIN_MODE=release。
Recovery 中间件会 recover 任何 panic。如果有 panic 的话,会写入 500 响应码。
如果不想使用上面两个默认的中间件,可以使用 gin.New()新建一个没有任何默认中间件的 路由。
6.2 gin中间件中使用 goroutine
当在中间件或 handler 中启动新的 goroutine 时,不能使用原始的上下文(c *gin.Context), 必须使用其只读副本(c.Copy())
1 2 3 4 5 6 7 8 9 10
| r.GET("/", func(c *gin.Context) { cCp := c.Copy() go func() { fmt.Println("Done! in path " + cCp.Request.URL.Path) }() c.String(200, "首页")
})
|